// 
//
// Author: Christopher Schwarzer
// Date: 12/2011
//
//
#ifndef Organism_H
#define Organism_H

#include <iostream>
#include <vector>
#include <utility>
#include "ShapeSequence.h"

namespace Shape
{

struct Tuple
{	
	friend std::ostream& operator<<(std::ostream& os, const Tuple& t);

	int x, y;
	Tuple() : x(0), y(0) {}
	Tuple(int x, int y) : x(x), y(y) {}
};

class Organism;

/// A node in the graph representation of organism shape. 
/// Use it for inspection only and modify the graph via the Organism class.
class Node
{
friend class Sequence;
friend class Organism;
friend std::ostream& operator<<(std::ostream&, const Node&);

public:
	// Returns true if this node has one or less neighbours
	bool IsLeaf() const;
	
	inline id_t GetID() const			 			{ return id; }
	// The position of this node, relative to the root as origin
	inline const Tuple& GetPos() const 				{ return pos; }
	// The rotation of this node, relative to the root facing front
	inline int GetRot() const 						{ return rot; }
	// Direction to the root node, or -1 if it is the root
	inline int GetSideToParent() const  			{ return parent; }
	inline Node* GetParent() const 					{ return (parent >= 0)? connections[parent] : NULL; }
	inline Node* GetConnection(side_t side) const 	{ return connections[side]; }
	
private:
	Node(id_t id);
	
	Tuple pos;
	id_t id;
	
	int rot;
	side_t parent;
	
	Node* connections[SIDE_COUNT];
	
	// Removes this from the shape only if this is a leaf, otherwise returns false
	bool RemoveLeaf();
	// Adds a new node to the given side only if the side is empty, otherwise returns null
	Node* AddLeaf(id_t id, side_t parentside, side_t childside);
	// Rotates this by the given amount of sides
	void Rotate(int bysides);
	
	Tuple GetPosOfSide(side_t side) const;
};

typedef std::vector<Node*> NodeList;

/// A graph representation of organism shape.
/// Use this class for initializing/building a specific shape,
/// modification, inspection and evolution. For de-/serialization
/// and morphogenesis, use Shape::Sequence, the serialized representation of the shape.
/// This class only allows tree-like shapes, that are collision free in a plane. The
/// size of the shape is limited by the maxSpread parameter. For example, with a 
/// maxSpread of 3, the entire shape has to fit within a grid of 7x7 robots with the root
/// in the center.
class Organism
{
friend class Sequence;
friend class Node;
friend std::ostream& operator<<(std::ostream&, const Organism&);

public:
	Organism(const Organism& original);
	/// \param maxSpread the maximum distance this shape can extend from its root.
	Organism(int maxSpread = 7);
	~Organism();

	inline Node* GetRoot() const				 { return nodeList[0]; }
	inline int Size() const 					 { return (int) nodeList.size(); }
	inline const NodeList& GetNodes() const 	 { return nodeList; }
	Node* GetNode(id_t id) const;

	id_t AddNode(id_t parent, side_t parentside, side_t childside);
	Node* AddNode(Node* parent, side_t parentside, side_t childside);
	
	void GetExtends(Tuple& min, Tuple& max) const;
	NodeList GetLeaves() const;
	
	void Mutate(int mutationCount = 1);
    void MutateFixed();
	Organism* CreateMutant(int mutationCount = 1) const;
	void AddRandom();
	void RemoveRandom();
	void RotateRandom();	// not implemented yet
	
	Organism* CreateRecombinant(const Organism& mate) const;	// not implemented yet
	
	bool Match(const Organism* other) const;

private:
	const int maxSpread;
	const int mapSize;
	NodeList nodeList;
	int nodeIDCount;
	uint8_t* map;
	
	void PostDeserializeInit(Node* node, side_t sideFromParent);
	
	int FindNodeIndex(Node* node) const;
	bool IsPosFree(const Tuple& p, side_t side);
	inline uint8_t* MapAt(int x, int y) const { return map + ((x * mapSize) + y); }
	inline uint8_t* MapPos(const Tuple& p) const { return map + (((p.x + maxSpread) * mapSize) + (p.y + maxSpread)); }
};

}

#endif
